前言

上一篇文章中介绍了自定义View的基本流程,今天我们仍然跟随洋神脚步学习自定义View,本篇文章参考自洋神Android 自定义View (三) 圆环交替 等待效果一文,有兴趣的可以去洋神的博客看看哈!

举例

自定义View流程回顾

上一篇文章中介绍了下自定义View的流程,在本篇文章中我们仍然将这4个步骤列出来加深印象:

  • 自定义View的属性
  • 在View的构造方法中获得自定义的属性
  • 重写onMeasure
  • 重写onDraw

下面我们按照这4个步骤一步步完成我们的自定义View。

自定义View的属性

在工程目录下的res/values子目录下建立一个attrs.xml文件,在里面定义我们需要的属性:

1
2
3
4
5
6
7
8
9
10
11
12
13
<?xml version="1.0" encoding="utf-8"?>
<resources>
<attr name="firstColor" format="color"/>
<attr name="secondColor" format="color"/>
<attr name="circleWidth" format="dimension"/>
<attr name="speed" format="integer"/>
<declare-styleable name="CustomView">
<attr name="firstColor"/>
<attr name="secondColor"/>
<attr name="circleWidth"/>
<attr name="speed"/>
</declare-styleable>
</resources>

如上面这段代码,我们定义了4个属性,分别是第一个圆环的颜色、第二个圆环的颜色、圆环的宽度以及圆环更新的速度,然后我们就可以在layout布局文件中这样使用我们的自定义View及自定义属性:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.glemontree.customview04.MainActivity">
<com.glemontree.customview04.CustomView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"/>
</RelativeLayout>

这里我没有添加自定义属性,全都使用的默认属性,如果你有兴趣,可以在layout布局文件中的CustomView子结点下添加app:xxx="xxx"来使用自定义属性。

在View的构造方法中获取自定义属性

紧接着我们可以在自定义View的构造方法中获取自定义的属性,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
public CustomView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CustomView, defStyleAttr, 0);
mFirstColor = a.getColor(R.styleable.CustomView_firstColor, Color.GREEN);
mSecondColor = a.getColor(R.styleable.CustomView_secondColor, Color.RED);
mCircleWidth = a.getDimensionPixelOffset(R.styleable.CustomView_circleWidth,
(int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 20, getResources().getDisplayMetrics()));
mSpeed = a.getInt(R.styleable.CustomView_speed, 20);
a.recycle();
mPaint = new Paint();
// 开启线程更新进度并刷新界面
new Thread() {
public void run() {
while (true) {
mProgress++;
if (mProgress == 360) {
mProgress = 0;
if (!mIsNext) {
mIsNext = true;
} else {
mIsNext = false;
}
}
// 刷新界面
postInvalidate();
// Thread.sleep()函数需要使用try-catch语句进行包围
try {
Thread.sleep(mSpeed);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
}

在View的构造方法中我们分别获取了我们自定义的4个属性,并且设置了默认值,这样即使在layout布局文件中不设置这4个自定义属性,仍然可以保证有值。

在构造方法中还开启了一个线程,这个线程主要的目的就是更新mProgress并且刷新界面,mProgress每增加1,就刷新界面一次,刷新界面是通过postInvalidate()方法来实现的。

因为这里并不需要onMeasure()操作,因此省去onMeasure()的实现,我们直接看onDraw()函数。

重写onDraw

onDraw()函数的内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
@Override
protected void onDraw(Canvas canvas) {
// 获得中心点
int centre = getWidth() / 2;
// 获得半径
int radius = centre - mCircleWidth / 2;
// 设置空心线宽
mPaint.setStrokeWidth(mCircleWidth);
// 抗锯齿
mPaint.setAntiAlias(true);
// 只绘制图形轮廓(描边)
mPaint.setStyle(Paint.Style.STROKE);
// 指定圆弧的外轮廓矩形区域
RectF oval = new RectF(centre - radius, centre - radius, centre + radius, centre + radius);
if (!mIsNext) {
// 设置圆环的颜色
mPaint.setColor(mFirstColor);
// 画出圆环
canvas.drawCircle(centre, centre, radius, mPaint);
// 设置圆弧的颜色
mPaint.setColor(mSecondColor);
// 根据进度画圆弧
canvas.drawArc(oval, -90, mProgress, false, mPaint);
} else {
// 设置圆环的颜色
mPaint.setColor(mSecondColor);
// 画出圆环
canvas.drawCircle(centre, centre, radius, mPaint);
// 设置圆弧的颜色
mPaint.setColor(mFirstColor);
// 根据进度画圆弧
canvas.drawArc(oval, -90, mProgress, false, mPaint);
}
// canvas.drawRect(oval, mPaint);
}

其实也没啥特别要说的,就是绘制圆环和圆弧,下面我想介绍下drawArc()函数。

`drawArc()函数的原型如下:

1
public void drawArc(RectF oval, float startAngle, float sweepAngle, boolean useCenter, Paint paint)

其各个参数的含义如下:

  • oval:指定圆弧的外轮廓矩形区域
  • startAngle:圆弧起始角度,单位为度
  • sweepAngle:圆弧扫过的角度,顺时针方向,单位为度
  • useCenter:当为true时,在绘制圆弧时将圆心包括在内,通常用来绘制扇形
  • paint:绘制圆弧的画笔

第一个参数oval我纠结了蛮久,一直没搞懂是什么意思,其实呢,就是圆弧的外切圆,比如在我们的程序中:

1
RectF oval = new RectF(centre - radius, centre - radius, centre + radius, centre + radius);

绘制的矩形正好是圆弧的外切矩形,在上面这段程序中你可以取消最后一行的注释,查看绘制的矩形到底是什么样的。

总结

其实我是想上一张动图的,但是尼玛使用GifCam录制动图得到的动图总是颜色凌乱,还没搞懂,等我搞懂了搞张图上来。